﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using Noris.Schedule.Support.Services;

namespace Noris.Schedule.Support
{
	/// <summary>
	/// Hlavní správce podkladových dat aplikace.
	/// Používá se pro získání dat trvalého charakteru i pro volání systémových metod.
	/// </summary>
	/// <remarks>
	/// Obsahuje privátní singleton na svoji instanci, v níž uchovává trvalá data.
	/// Uchovává v sobě konfiguraci, reflexi výkonných tříd.
	/// </remarks>
	public partial class Steward
	{
		#region SYSTÉM DEKLARACÍ GRAFŮ - PŘÍSTUP PŘES STEWARDA
		/// <summary>
		/// Sada deklarací grafů
		/// </summary>
		public static GraphDeclarationSetCls GraphDeclarationSet { get { return Current._GraphDeclarationSet; } }
		#endregion
		#region SYSTÉM DEKLARACÍ GRAFŮ - KONSTRUKCE
		/// <summary>
		/// Instanční přístup k sadě deklarací grafů
		/// </summary>
		private GraphDeclarationSetCls _GraphDeclarationSet
		{
			get
			{
				if (this.__GraphDeclarationSet == null)
				{
					lock (__GraphDeclarationLock)
					{
						if (this.__GraphDeclarationSet == null)
							this.__GraphDeclarationSet = new GraphDeclarationSetCls();
					}
				}
				return this.__GraphDeclarationSet;
			}
		}
		/// <summary>
		/// Úložiště objektu, který v sobě obsahuje deklarace grafů
		/// </summary>
		private GraphDeclarationSetCls __GraphDeclarationSet;
		/// <summary>
		/// Locker pro tvorbu objektu, který v sobě obsahuje deklarace grafů
		/// </summary>
		private object __GraphDeclarationLock = new object();
		#endregion
	}
	#region GraphDeclarationSetCls : Správce deklarací grafů, poskytnutých službou IGraphDeclarator
	/// <summary>
	/// Třída GraphDeclarationSetCls : Správce deklarací grafů, poskytnutých službou IGraphDeclarator.
	/// Po jejím instancování jsou načteny deklarace grafů pomocí detekovaných Service typu IGraphDeclarator.
	/// Datové zdroje mají mít implementované služby DataLoad a DataProcess, které nejsou v rámci instancování vyvolány.
	/// Uvedené služby je možno vyvolat centrálně pomocí volání metody this.CallRequestAll() později.
	/// </summary>
	public class GraphDeclarationSetCls
	{
		#region KONSTRUKCE
		internal GraphDeclarationSetCls()
		{
			this._Locker = new object();
			this._DeclarationList = new List<GraphDeclarationItemCls>();
			this._DeclaredDataSourceList = new List<IDataSource>();
			this._LoadDeclarations();
		}
		private object _Locker;
		/// <summary>
		/// Soupis deklarací jednotlivých grafů
		/// </summary>
		private List<GraphDeclarationItemCls> _DeclarationList;
		/// <summary>
		/// Soupis datových zdrojů, každý objekt jen jedenkrát (různé instance : každá samostatně)
		/// </summary>
		private List<IDataSource> _DeclaredDataSourceList;
		#endregion
		#region PROPERTY
		/// <summary>
		/// Seznam deklarací. Načtený, setříděný.
		/// </summary>
		public List<GraphDeclarationItemCls> DeclarationList { get { return this._DeclarationList; } }
		#endregion
		#region NAČTENÍ DEKLARACÍ
		/// <summary>
		/// Metoda načte deklarace grafů ze všech přítomných deklarátorů, a seznam setřídí.
		/// </summary>
		private void _LoadDeclarations()
		{
			lock (this._Locker)
			{
				this._DeclarationList = new List<GraphDeclarationItemCls>();
				IEnumerable<IGraphDeclarator> declaratorList = Steward.GetServiceObjectList<IGraphDeclarator>(true);
				List<IGraphDeclarator> scanDeclaratorList = new List<IGraphDeclarator>(declaratorList);
				while (scanDeclaratorList.Count > 0)
				{
					// Seznam deklarátorů, kteří se odkazují na externí datový zdroj, dosud neexistující:
					List<IGraphDeclarator> nextScanList = new List<IGraphDeclarator>();

					// Projdu všechny deklarátory, kteří dosud nejsou vyřízení:
					foreach (IGraphDeclarator declarator in scanDeclaratorList)
					{
						GraphDeclareArgs args = new GraphDeclareArgs(this._GetDataSource, this._TryGetDataSource);
						try
						{
							// Deklarátor (declarator) může požadovat vyhledání externího datového zdroje, při tom může dojít k chybě IdsException.
							//  To zajistí metoda 
							declarator.GraphDeclare(args);

							if (args.Priority > 0F && args.GraphDeclarationList != null && args.GraphDeclarationList.Count > 0)
							{
								int order = 0;
								Type declaratorType = declarator.GetType();
								foreach (GraphDeclarationCls graphDeclaration in args.GraphDeclarationList)
								{
									this._DeclarationList.Add(new GraphDeclarationItemCls(declaratorType, args.Priority, ++order, graphDeclaration));
									this._AddDataSource(graphDeclaration.DataSource);       // Přidám datový zdroj do evidence
								}
							}

						}
						catch (IdsException exc)
						{	// Chyba, když (declarator) se odkazuje na externí datový zdroj, který dosud nikdo nezaregistroval:
							// Tak v tom případě přidám objekt (declarator) do soupisu těch, které projdeme v příštím cyklu.
							nextScanList.Add(declarator);
						}
					}

					// Jak jsme dopadli:
					if (nextScanList.Count == 0) break;                  // Na příští cyklus nemám nikoho: hotovo, OK
					if (nextScanList.Count == scanDeclaratorList.Count)  // Na příští cyklus mám úplně všechny => nic se nevyřešilo, nic se ani nebyřeší, chyba:
					{
						string declaratorSum = "";
						foreach (IGraphDeclarator declarator in nextScanList)
							declaratorSum += "\r\n" + declarator.GetType().Namespace + "." + declarator.GetType().Name;
						Throw.SysError(MessageInfo.Get("Chyba v deklarátorech grafů: některé deklarátory se odkazují na externí datový zdroj, který nikdo nenadefinoval.\r\n" + declaratorSum));
						break;
					}
					// V soupisu nextScanList je několik deklarátorů (více než 0 a méně než počet všech deklarátorů),
					// kteří se odvolávají na nějaký dosud neexistující externí datový zdroj.
					// A přitom několik deklarátorů se úspěšně zaregistrovalo.
					// Je tedy šance, že pro deklarátory odložené do seznamu nextScanList se nyní podaří najít jejich externí datový zdroj.
					// Spustím další loop této smyčky, v němž projdeme deklarátory ze soupisu nextScanList:
					scanDeclaratorList = nextScanList;
				}
				this._DeclarationList.Sort(GraphDeclarationItemCls.CompareByOrder);
			}
		}
		#endregion
		#region VYHLEDÁNÍ EXTERNÍCH DATOVÝCH ZDROJŮ
		/// <summary>
		/// Metoda najde a vrátí datový zdroj daného typu. Pokud neexistuje, vyhodí výjimku IdsException.
		/// </summary>
		/// <param name="dataSourceType">Typ datového zdroje</param>
		/// <returns>Datový zdroj</returns>
		private IDataSource _GetDataSource(Type dataSourceType)
		{
			IDataSource ds = _TryGetDataSource(dataSourceType);
			if (ds == null) throw new IdsException();              // Pokud nenajdu, hodím tuhle chybu. Nejde o text chyby, ale o její typ. Ten se odchytí v _LoadDeclarations(), viz tam, o pár řádků výše.
			return ds;
		}
		/// <summary>
		/// Metoda najde a vrátí datový zdroj daného typu. Pokud neexistuje, vrátí null.
		/// </summary>
		/// <param name="dataSourceType">Typ datového zdroje</param>
		/// <returns>Datový zdroj</returns>
		private IDataSource _TryGetDataSource(Type dataSourceType)
		{
			IDataSource findDs = this._DeclaredDataSourceList.Find(delegate(IDataSource ds) { return (ds.GetType() == dataSourceType);});
			return findDs;
		}
		/// <summary>
		/// Do interního seznamu datových zdrojů přidám další datový zdroj.
		/// Každou instanci jen jedenkrát, ale různé instance přidám pokaždé i když jsou stejného typu.
		/// Null nepřidám.
		/// </summary>
		/// <param name="dataSource"></param>
		private void _AddDataSource(IDataSource dataSource)
		{
			// Hodnotu null neberu:
			if (dataSource == null) return;
			// Pokud předaný datový zdroj už mám v evidenci (fyzicky tu samou instanci), pak ji podruhé neukládám:
			if (this._DeclaredDataSourceList.Exists(delegate(IDataSource ds) { return Object.ReferenceEquals(ds, dataSource); }))
				return;
			// Poprvé, když nenajdu danou instanci, tak si ji uložím:
			this._DeclaredDataSourceList.Add(dataSource);
		}
		#endregion
	}
	#endregion
	#region CLASS GraphDeclarationItemCls : Jedna položka deklarace grafu, obsahuje základní deklaraci z deklarátorů plus režijní data
	/// <summary>
	/// Třída GraphDeclarationItemCls : Jedna položka deklarace grafu, obsahuje základní deklaraci z deklarátorů plus režijní data
	/// </summary>
	public class GraphDeclarationItemCls
	{
		#region KONSTRUKCE
		internal GraphDeclarationItemCls(Type declaratorType, float priority, int order, GraphDeclarationCls graphDeclaration)
		{
			this._GraphID = ++_LastGraphID;
			this._DeclaratorType = declaratorType;
			this._DeclaratorPriority = priority;
			this._DeclarationListOrder = order;
			this._GraphDeclaration = graphDeclaration;
		}
		private static int _LastGraphID = 0;
		private int _GraphID;
		private Type _DeclaratorType;
		private float _DeclaratorPriority;
		private int _DeclarationListOrder;
		private GraphDeclarationCls _GraphDeclaration;
		public override string ToString()
		{
			return this._GraphDeclaration.ToString() + "  {Declarator=" + this.DeclaratorType.FullName + "}";
		}
		#endregion
		#region PROPERTY
		/// <summary>
		/// ID tohoto grafu, automaticky se přiřazuje počínaje 1
		/// </summary>
		public int GraphID { get { return this._GraphID; } }
		/// <summary>
		/// Deklarace grafu z jeho deklarátoru
		/// </summary>
		public GraphDeclarationCls GraphDeclaration { get { return this._GraphDeclaration; } set { this._GraphDeclaration = value; } }
		/// <summary>
		/// Type deklarátora
		/// </summary>
		public Type DeclaratorType { get { return this._DeclaratorType; } }
		/// <summary>
		/// Priorita daná deklarátorem, menší číselná hodnota bude ve vizualiaci dříve (nahoře)
		/// </summary>
		public float DeclaratorPriority { get { return this._DeclaratorPriority; } }
		/// <summary>
		/// Pořadí deklarace v rámci seznamu z výstupu jednoho deklarátoru
		/// </summary>
		public int DeclarationListOrder { get { return this._DeclarationListOrder; } }
		// Property z vestavěné deklarace:
		/// <summary>
		/// Výchozí režim grafu
		/// </summary>
		public RowGraphMode DefaultGraphMode { get { return this._GraphDeclaration.DefaultGraphMode; } }
		/// <summary>
		/// Výchozí číslo třídy. Pokud bude kladné, předá se do datového zdroje pro načtení všech řádků této třídy.
		/// </summary>
		public int RowClassNumber { get { return this._GraphDeclaration.RowClassNumber; } }
		/// <summary>
		/// Seznam čísel záznamů (číslo třídy a číslo záznamu) pro výchozí položky do zobrazení grafu
		/// </summary>
		public List<GID> RowList { get { return this._GraphDeclaration.RowList; } }
		/// <summary>
		/// Objekt datového zdroje. Pokud má více grafů sdílet jeden datový zdroj, 
		/// musí se definice grafů zadat najednou a s jedním objektem datového zdroje.
		/// </summary>
		public IDataSource DataSource { get { return this._GraphDeclaration.DataSource; } }
		/// <summary>
		/// Libovolná informace předávaná spolu s grafem do požadavků do datového zdroje v requestu (v request.Tag)
		/// </summary>
		public object Tag { get { return this._GraphDeclaration.Tag; } set { this._GraphDeclaration.Tag = value; } }
		/// <summary>
		/// Popisek grafu. 
		/// U grafu na celou stránku, anebo v horní polovině stránky jde o text na záložce, 
		/// u grafu v dolní polovině je text uveden na dělítku (Splitter).
		/// </summary>
		public string GraphCaption { get { return this._GraphDeclaration.GraphCaption; } }
		/// <summary>
		/// Požadovaná pozice grafu.
		/// </summary>
		public GraphPositionType GraphPosition { get { return this._GraphDeclaration.GraphPosition; } }
		/// <summary>
		/// Barva záložky / dělítka
		/// </summary>
		public Color TabColor { get { return this._GraphDeclaration.TabColor; } }
        /// <summary>
        /// Barva pozadí řádku
        /// </summary>
        public Color RowColor { get { return this._GraphDeclaration.RowColor; } }
        /// <summary>
		/// Nejvyšší výška řádku grafu, v pixelech.
		/// </summary>
		public int RowHeightTotalMaxPx { get { return this._GraphDeclaration.RowHeightTotalMaxPx; } }
        /// <summary>
        /// Nejmenší výška řádku grafu, v pixelech.
        /// </summary>
        public int RowHeightTotalMinPx { get { return this._GraphDeclaration.RowHeightTotalMinPx; } }
        /// <summary>
		/// Typická výška jednoho řádku.
		/// </summary>
		public int RowHeightLineSolidPx { get { return this._GraphDeclaration.RowHeightLineSolidPx; } }
		/// <summary>
		/// Typická výška jednoho řádku, pokud jde o řádek typu RowGraphMode.TaskCapacityLink.
		/// </summary>
		public int RowHeightLineFragmentPx { get { return this._GraphDeclaration.RowHeightLineFragmentPx; } }
		/// <summary>
		/// Výška nejmenšího zlomku v grafu typu RowGraphMode.TaskCapacityLink, kdy jsou zobrazeny položky využívající podíl kapacity.
		/// </summary>
		public int RowHeightMinFragmentPx { get { return this._GraphDeclaration.RowHeightMinFragmentPx; } }
		/// <summary> { get; }
		/// Přídavek k výšce neděleného řádku, vyjádřený relativně k hodnotě this.RowHeightOneLine (fyzická výška jedné linky).
		/// </summary>
		public decimal RowHeightAddLineSolidRatio { get { return this._GraphDeclaration.RowHeightAddLineSolidRatio; } }
		/// <summary> { get; }
		/// Přídavek k výšce děleného řádku, vyjádřený relativně k hodnotě this.RowHeightOneLine (fyzická výška jedné linky).
		/// </summary>
		public decimal RowHeightAddLineFragmentRatio { get { return this._GraphDeclaration.RowHeightAddLineFragmentRatio; } }
		/// <summary>
		/// Vrstva, kam se vykreslí linky časové osy v tomto grafu.
		/// Default = SubElements.
		/// </summary>
		public GraphLinePosition TimeTickLayer { get { return this._GraphDeclaration.TimeTickLayer; } }
		/// <summary>
		/// Vrstva, kam se vykreslí linky jednotek v grafu kapacitních jednotek, kde jsou kresleny fragmenty (částečné = podílové využití kapacity).
		/// Default = SubElements.
		/// </summary>
		public GraphLinePosition FragmentLineLayer { get { return this._GraphDeclaration.FragmentLineLayer; } }
		/// <summary>
		/// Kreslit síť vztahů kolem elementu, na který se kliknulo.
		/// Default = true.
		/// </summary>
		public bool DrawElementRelationsOnClick { get { return this._GraphDeclaration.DrawElementRelationsOnClick; } }
		/// <summary>
		/// Kreslit síť vztahů kolem elementu, který se právě edituje.
		/// Default = true.
		/// </summary>
		public bool DrawElementRelationsInEditing { get { return this._GraphDeclaration.DrawElementRelationsInEditing; } }
		/// <summary>
		/// Režim kreslení linků = vztahy mezi svázanými úkoly
		/// </summary>
		public LinkElementDrawMode DrawLinkMode { get { return this._GraphDeclaration.DrawLinkMode; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení pozice sousedních grafů, do kterých se předává událost DataChanged na tomto grafu.
		/// Default = Anywhere.
		/// </summary>
		public TargetGraphCrossActivityMode TargetGraphActivityOnDataChanged { get { return this._GraphDeclaration.TargetGraphActivityOnDataChanged; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení pozice sousedních grafů, do kterých se předává událost RowEnter na tomto grafu.
		/// Default = None.
		/// </summary>
		public TargetGraphCrossActivityMode TargetGraphActivityOnRowEnter { get { return this._GraphDeclaration.TargetGraphActivityOnRowEnter; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení pozice sousedních grafů, do kterých se předává událost Click na tomto grafu.
		/// Default = Anywhere.
		/// </summary>
		public TargetGraphCrossActivityMode TargetGraphActivityOnClick { get { return this._GraphDeclaration.TargetGraphActivityOnClick; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení pozice sousedních grafů, do kterých se předává událost DoubleClick na tomto grafu.
		/// Default = Anywhere.
		/// </summary>
		public TargetGraphCrossActivityMode TargetGraphActivityOnDoubleClick { get { return this._GraphDeclaration.TargetGraphActivityOnDoubleClick; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení reakce tohoto grafu na událost DataChanging v jiném grafu (aktivita v průběhu editace = při tažení elementu po grafu).
		/// Pro upřesnění: jiný graf právě edituje svoje data, a dosud nevyvolal request DataSourceRequestType.EventAfterEdit do datového zdroje.
		/// Jen mění svoje data na vizuální vrstvě.
		/// Náš graf je v tuto chvíli pasivním příjemcem této editace, a jen má reagovat na editovaná data.
		/// Default = CurrentGraphCrossActivityMode.None
		/// Poznámka: target aktivita se neurčuje, je implicitní na hodnotě OnThisPage (tento event se zásadně předává jen do sousedního grafu na téže stránce).
		/// </summary>
		public CurrentGraphCrossActivityMode CurrentGraphActivityOnDataChanging { get { return this._GraphDeclaration.CurrentGraphActivityOnDataChanging; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení reakce tohoto grafu na událost DataChanged v jiném grafu.
		/// Pro upřesnění: jiný graf editoval svoje data, a na závěr zavolal svůj datový zdroj, jemuž předal request DataSourceRequestType.EventAfterEdit.
		/// Náš graf je v tuto chvíli pasivním příjemcem této editace, a jen má reagovat na editovaná data.
		/// Default = CurrentGraphCrossActivityMode.DefaultEdit
		/// </summary>
		public CurrentGraphCrossActivityMode CurrentGraphActivityOnDataChanged { get { return this._GraphDeclaration.CurrentGraphActivityOnDataChanged; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení reakce tohoto grafu na událost RowEnter na řádku v jiném grafu.
		/// Default = None.
		/// </summary>
		public CurrentGraphCrossActivityMode CurrentGraphActivityOnRowEnter { get { return this._GraphDeclaration.CurrentGraphActivityOnRowEnter; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení reakce tohoto grafu na událost Click na elementu v jiném grafu.
		/// Default = CurrentGraphCrossActivityMode.Default
		/// </summary>
		public CurrentGraphCrossActivityMode CurrentGraphActivityOnClick { get { return this._GraphDeclaration.CurrentGraphActivityOnClick; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Určení reakce tohoto grafu na událost DoubleClick na elementu v jiném grafu.
		/// Default = CurrentGraphCrossActivityMode.Default
		/// </summary>
		public CurrentGraphCrossActivityMode CurrentGraphActivityOnDoubleClick { get { return this._GraphDeclaration.CurrentGraphActivityOnDoubleClick; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Má se volat datový zdroj při aktivaci stránky s tímto grafem?
		/// Default = false
		/// </summary>
		public bool CurrentGraphCallDataSourceOnActivate { get { return this._GraphDeclaration.CurrentGraphCallDataSourceOnActivate; } }
		/// <summary>
		/// Definice mezigrafové aktivity:
		/// Má se volat datový zdroj při deaktivaci stránky s tímto grafem?
		/// Default = false
		/// </summary>
		public bool CurrentGraphCallDataSourceOnDeactivate { get { return this._GraphDeclaration.CurrentGraphCallDataSourceOnDeactivate; } }

		#endregion
		#region KOMPARÁTOR
		/// <summary>
		/// Komparátor dvou záznamů třídy GraphDeclarationItemCls podle vhodného pořadí v soupisu grafů
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		internal static int CompareByOrder(GraphDeclarationItemCls a, GraphDeclarationItemCls b)
		{
			int cmp = a._DeclaratorPriority.CompareTo(b._DeclaratorPriority);        // Nejdříve vyšší priorita = nižší číslo
			if (cmp != 0) return cmp;
			cmp = a._DeclaratorType.FullName.CompareTo(b._DeclaratorType.FullName);  // Pak pro jistotu podle jména deklarátoru
			if (cmp != 0) return cmp;
			cmp = a._DeclarationListOrder.CompareTo(b._DeclarationListOrder);        // Podle pořadí v deklarativním seznamu
			return cmp;
		}
		#endregion
	}
	#endregion
}
